home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / program / recio215.zip / DESIGN.TXT < prev    next >
Text File  |  1996-10-26  |  18KB  |  404 lines

  1.     Title: RECIO DESIGN AND DEVELOPMENT NOTES
  2. Copyright: (C) 1994-1996, William Pierpoint
  3.   Version: 2.15
  4.      Date: October 26, 1996
  5.  
  6.  
  7. 1.0 DATA STRUCTURES
  8.  
  9. 1.1 REC structure for each record stream
  10.  
  11.     * defined in recio.h.
  12.     * 3 (!dos) or 4 (dos) static RECs for recin, recout, etc (included in
  13.       ROPEN_MAX count).
  14.     * allocate dynamic array of RECs dimensioned to ROPEN_MAX-NREC in ropen().
  15.     * Each REC has two associated buffers:
  16.       1) record string buffer containing current record;
  17.          allocate when first record read;
  18.          reallocate if record becomes larger.
  19.       2) field string buffer containing current field;
  20.          allocate when first field read;
  21.          reallocate if field becomes larger.
  22.     * deallocate dynamic RECs and associated buffers in rclose() and
  23.       rcloseall() if all record streams closed; deallocate associated
  24.       buffers for recin with an exit function registered with atexit().
  25.  
  26.  
  27. 1.2 REC r_flags assignments
  28.  
  29.     Bit        Description
  30.    -----    -------------------------------------------------------------
  31.      0        If clear, colno start at 0; if set, colno start at 1
  32.      1      if clear, read mode; if set, write/append mode
  33.      2      if clear, current field not quoted by r_txtch;
  34.             if set, current field quoted by r_txtch.
  35.     3-6     Reserved for future use.
  36.      7        If clear, EOF not reached; if set, EOF reached
  37.     8-11    If clear, no error; else rerror number
  38.    12-15    if clear, no warning; else rwarning number
  39.  
  40.  
  41. 1.3 Accessing and Setting Information
  42.  
  43.     How do I
  44.     * access the name of the record stream?           rnames()
  45.     * access the current context number?              rcxtno()
  46.     * access the current record number?               rrecno()
  47.     * access the current field number?                rfldno()
  48.     * access the current column number?               rcolno()
  49.     * access the record string buffer?                rrecs()
  50.     * access the field string buffer?                 rflds()
  51.     * go to a specific field?                         rgotofldno()
  52.     * determine the number of fields in a record?     rnumfld()
  53.     * determine if column numbers start at 0 or 1?    rbegcolno()
  54.     * determine if the current field was quoted?      ristxtfld()
  55.     * determine if there are more records left?       reof()
  56.     * determine if there is an error on the stream?   rerror()
  57.     * determine if there is a warning on the stream?  rwarning()
  58.     * access the error message for the stream?        rerrstr()
  59.     * access the last warning message on the stream?  rwarnstr()
  60.     * force an error on a record stream?              rseterr()
  61.     * clear an error on a record stream?              rclearerr()
  62.     * force a warning on a record stream?             rsetwarn()
  63.     * clear a warning on a record stream?             rclearwarn()
  64.     * scan the record buffer more than once?          rresetrec()
  65.     * increase the size of the record string buffer?  rsetrecsiz()
  66.     * increase the size of the field string buffer?   rsetfldsiz()
  67.     * replace the data in the field string buffer?    rsetfldstr()
  68.     * replace the data in the record string buffer?   rsetrecstr()
  69.     * get the position of a field in a record?        rgetfldpos()
  70.     * return to a previous field position?            rsetfldpos()
  71.     * set the field delimiter character?              rsetfldch()
  72.     * set the text delimiter character?               rsettxtch()
  73.     * set the time format string?                     rsettmfmt()
  74.     * set the context number?                         rsetcxtno()
  75.     * set column numbering to start at 0 or 1?        rsetbegcolno()
  76.     * dynamically copy a string?                      scpys()
  77.     * dynamically concatenate a string?               scats()
  78.     * trim white space from the ends of a string?     strims()
  79.  
  80.  
  81.  
  82. 2.0 CODE STRUCTURES
  83.  
  84. 2.1 RECIO Structure Chart
  85.  
  86.     ╔═════════╗                  ╔═════════╗
  87.     ║ rstr.c  ║                  ║ recio.c ║
  88.     ╚════╤════╝                  ╚═╤══╤══╤═╝
  89.          │                         │  │  │
  90.          │    ╔════════╗     input │  │  │ output   ╔════════╗
  91.          └────╢ rget.c ╟───────────┘  │  └──────────╢ rput.c ║
  92.        char   ╚═╤════╤═╝ column       │      char   ╚═╤════╤═╝ column
  93.      delimited  │    │  delimited     │    delimited  │    │  delimited
  94.     ╔═════════╗ │    │ ╔══════════╗   │   ╔═════════╗ │    │ ╔══════════╗
  95.     ║ rgetf.c ╟─┤    ├─╢ rcgetf.c ║   │   ║ rputf.c ╟─┤    ├─╢ rcputf.c ║
  96.     ╚═════════╝ │    │ ╚══════════╝   │   ╚═════════╝ │    │ ╚══════════╝
  97.                 │    │                │               │    │
  98.     ╔═════════╗ │    │ ╔══════════╗   │   ╔═════════╗ │    │ ╔══════════╗
  99.     ║ rgets.c ╟─┤    ├─╢ rcgets.c ║   │   ║ rputs.c ╟─┤    ├─╢ rcputs.c ║
  100.     ╚═════════╝ │    │ ╚══════════╝   │   ╚═════════╝ │    │ ╚══════════╝
  101.                 │    │                │               │    │
  102.     ╔═════════╗ │    │ ╔══════════╗   │   ╔═════════╗ │    │ ╔══════════╗
  103.     ║ rgett.c ╟─┤    ├─╢ rcgett.c ║   │   ║ rputt.c ╟─┤    ├─╢ rcputt.c ║
  104.     ╚═════════╝ │    │ ╚══════════╝   │   ╚═════════╝ │    │ ╚══════════╝
  105.                 │    │                │               │    │
  106.     ╔═════════╗ │    │ ╔══════════╗   │   ╔═════════╗ │    │ ╔══════════╗
  107.     ║ rbget.c ╟─┘    └─╢ rcbget.c ║   │   ║ rbput.c ╟─┘    └─╢ rcbput.c ║
  108.     ╚═════════╝        ╚══════════╝   │   ╚═════════╝        ╚══════════╝
  109.                                       │
  110.                        ╔══════════╗   │   ╔═════════╗        ╔══════════╗
  111.                        ║ rwarn.c  ╟───┴───╢ rerr.c  ╟────────╢  rfix.c  ║
  112.                        ╚════╤═════╝       ╚════╤════╝        ╚══════════╝
  113.                             │                  │
  114.                        ╔════╧═════╗       ╔════╧════╗
  115.                        ║ rwmsg.c  ║       ║ remsg.c ║
  116.                        ╚══════════╝       ╚═════════╝
  117.  
  118.  
  119. 2.2 Callback Error Functions
  120.  
  121.     The callback error function technique used by the RECIO library can
  122.     be applied within your programs to other abstract data types.  For
  123.     abstract data types within the standard C library, you will need to
  124.     write wrapper functions.
  125.  
  126.                                  ╔═════════╗
  127.                                  ║ errnofn ║
  128.                                  ╚════╤════╝
  129.                                       │
  130.                        ┌──────────────┼──────────────┬───── etc
  131.                        │              │              │
  132.                   ╔════╧════╗    ╔════╧════╗    ╔════╧════╗
  133.                   ║ rerrfn  ║    ║ ferrfn  ║    ║ merrfn  ║
  134.                   ╚═════════╝    ╚═════════╝    ╚═════════╝
  135.              REC            FILE          MEMORY
  136.  
  137.  
  138. 2.2.1 Errno Callback Error Function Skeleton [errnofn()]
  139.  
  140.     switch on error number [errno]
  141.     case out of memory [ENOMEM]
  142.     case out of range [ERANGE]
  143.     case out of record or file pointers [EMFILE]
  144.     case permission denied [EACCES]
  145.     case invalid argument [EINVAL]
  146.     default [anything else]
  147.     endcase
  148.  
  149.  
  150. 2.2.2 RECIO Callback Error Function Skeleton [rerrfn(rp)]
  151.  
  152.     if valid record pointer [risvalid(rp)]
  153.         if past end of file [reof(rp)]  (if reof test removed, past EOF will
  154.         else [error number set]           become R_EMISDAT or R_WEMPSTR)
  155.             switch on error number [rerror(rp)]
  156.             case read data errors [R_ERANGE || R_EINVDAT || R_EMISDAT]
  157.             case write data errors [R_ENOPUT]
  158.                 switch on context number [rcxtno(rp)]
  159.                 case RECIN
  160.                     switch field number [rfldno(rp)]
  161.                     case 1 (first field read)
  162.                     case 2 (second field read)
  163.                     ...
  164.                     endcase
  165.                 ...
  166.                 default [missing or unknown context number]
  167.                 endcase
  168.             case out of memory [R_ENOMEM]
  169.             case reading binary file [R_ENOGET]
  170.             case fatal errors [R_EINVAL || R_EINVMOD]
  171.             default [anything else]
  172.             endcase
  173.         endif
  174.     else [invalid record pointer]
  175.         call error function that handles errno errors [errnofn()]
  176.     endif
  177.  
  178.  
  179. 2.3 RECIO Callback Warning Function Skeleton [rwarnfn(rp)]
  180.  
  181.     if valid record pointer [risvalid(rp)]
  182.         switch on warning number [rwarning(rp)]
  183.         case read data warnings [R_WEMPSTR || R_WTMFMT]
  184.         case write data warnings [R_WWIDTH]
  185.         case code runtime warnings [R_WNOREG]
  186.         default [anything else]
  187.         endcase
  188.     endif
  189.  
  190.  
  191. 2.4 RECIO Field Parsing Algorithm
  192.  
  193. The RECIO library parses a field from an input record according to 
  194. regular expressions that are specified below using PERL programming 
  195. language notation (see the references in section 4.0).  Four cases must 
  196. be considered that depend on whether the field and text delimiters are 
  197. set to whitespace or to something else.  To illustrate the cases below, 
  198. that something else is the comma character for the field delimiter and 
  199. the double quote character for the text delimiter.
  200.  
  201. case 1: fldch = ' '; txtch = ' ';
  202.         \s*(\S*)(\s*(\s|$))
  203.  
  204. case 2: fldch = ','; txtch = ' ';
  205.         ([^,]*)(,|$)
  206.  
  207. case 3: fldch = ' '; txtch = '"'; 
  208.      A: \s*"(([^"]|"[^"]*"|"\S)*"?)("\s*(\s|$))
  209.      B: \s*"(([^"]|"\S)*"?)("\s*(\s|$))
  210.  
  211.         notes: (1) use case 3 when first nonwhitespace character is txtch; 
  212.                    otherwise use case 1.
  213.                (2) use case 3A when txtch is the double quote character;
  214.                    use the form of case 3B for any other nonwhitespace 
  215.                    text delimiter. (See case 4, note 2 example.)
  216.  
  217. case 4: fldch = ','; txtch = '"';
  218.      A: \s*"(([^"]|"[^"]*"|"\s*[^,])*"?)("\s*(,|$))
  219.      B: \s*"(([^"]|"\s*[^,])*"?)("\s*(,|$))
  220.  
  221.         notes: (1) use case 4 when first nonwhitespace character is txtch; 
  222.                    otherwise use case 2.
  223.                (2) use case 4A when txtch is the double quote character;
  224.                    use the form of case 4B for any other nonwhitespace 
  225.                    text delimiter.  (Example: "use case "4A", okay?", is 
  226.                    one field using case 4A, but would break after the 
  227.                    first comma if using case 4B.  Case 4A is limited to 
  228.                    double quote characters since other delimiters might 
  229.                    not be used in matching pairs.  For instance, the 
  230.                    single quote character is also used as an apostrophe.)
  231.  
  232. When the parsing algorithm is finished, the field buffer contains the 
  233. equivalent of the PERL variable $1.  The field parsing code can be 
  234. found in the _rfldlen and _rfldstr functions in rget.c.
  235.  
  236.  
  237.  
  238. 3.0 DEVELOPMENT NOTES
  239.  
  240. 3.1 fgets (Microsoft C 5.1)
  241.  
  242. Previous notes of mine indicate that Microsoft's fgets function does not
  243. work correctly when it reads a line of text that consists of only a newline.
  244. However this can be worked around by first setting the string buffer to an
  245. empty string.  If you plan on retaining the newline, you will need to test
  246. this further.  The fgets function is used in the rgetrec function.  
  247.  
  248.  
  249. 3.2 fopen (Borland C 3.1)
  250.  
  251. fopen() calls __openfp() calls open().  Borland's "Library Reference"
  252. documents error numbers for open(), but not for fopen().  These error
  253. numbers are ENOENT, EMFILE, EACCES, and EINVACC.  Because ropen() screens
  254. the access code, the EINVACC error should not occur from the recio library.
  255.  
  256.  
  257. 3.3 strtol & strtoul (Borland C 3.1)
  258.  
  259. These functions stop consuming input once they overflow, setting ERANGE.
  260. Hence endptr can point into the middle of a sequence of valid characters
  261. having the expected form as given in ANSI X3.159-1989, Sections 4.10.1.5
  262. and 4.10.1.6.  IMHO this characteristic is not in conformance with the
  263. ANSI standard as endptr should only point to the first unrecognized
  264. character or to the terminating null character.  Borland's strtod does
  265. not have this problem.
  266.  
  267. Note that ANSI X3.159-1989 Section 4.10.1.6 allows strtoul (unsigned
  268. long) to have an optional negative sign.  A negative unsigned long?!
  269. Borland 3.1 strtoul converts a negative long to an unsigned number
  270. without error.  But I prefer to trap any negative numbers input to
  271. unsigned fields.  So str2ul is a wrapper function for strtoul that
  272. first tests for a negative number and if one is found, flags the data
  273. as invalid and returns zero.
  274.  
  275. The test suite includes -0 as a data value.  The strtol function traps
  276. this as an ERANGE error and returns the overflow limit.  The rfixi and
  277. rfixl functions substitute zero.
  278.  
  279. 3.4 strtod (Borland C 3.1)
  280.  
  281. Unlike the strtol and strtoul functions, strtod does not clear errno first.
  282. Starting with recio vers. 2.02, errno is cleared before any conversion
  283. where recio code checks the value of errno after the conversion.  It is also
  284. cleared when a record stream is successfully opened.
  285.  
  286. 3.5 mktime (Borland C 3.1)
  287.  
  288. The Borland C 3.1 mktime function has valid range from 1 Jan 1970 00:00:00 
  289. to 19 Jan 2038 03:14:07.  Actual upper limit can vary by a few hours 
  290. depending on the timezone setting.  For EST, upper limit is 18 Jan 2038 
  291. 22:14:07.  When the recio library reads a 2-digit year, it first represents 
  292. it in a struct tm from 1951 to 2050.  (You can control this with the rsetbegyr 
  293. function.)  The mktime function is used to convert to a time_t type.  Hence, 
  294. when compiled with the Borland C 3.1 compiler, the recio library time_t 
  295. routines fail for any dates beyond 18 or 19 Jan 2038, or before 1 Jan 1970.
  296.  
  297. The minimum and maximum values of time_t are controlled by the recio.h
  298. symbolic constants TIME_T_MIN and TIME_T_MAX.  These are used only by 
  299. the time functions in rfix.c.  Borland C 3.1 represents time_t using 
  300. a long.  Thus TIME_T_MIN is set to 0 and TIME_T_MAX is set to LONG_MAX.
  301. LONG_MAX is defined in your compiler's header file limits.h.  You should 
  302. find your compiler's representation of time_t in your compiler's header 
  303. file time.h.
  304.  
  305. 3.6 ltoa and ultoa (ANSI-C)
  306.  
  307. Your ANSI-C compiler might lack the ltoa and ultoa functions as these are 
  308. missing from ANSI X3.159-1989.  These functions convert a long and an
  309. unsigned long to a string.  The recio library uses these functions.  If 
  310. they are missing from your compiler, you can write a simple version of
  311. these functions by using the sprintf function.  The sprintf function limits 
  312. the radix (base) to 8, 10, and 16, but this will probably be adequate for 
  313. most applications.  The prototypes for recio.h are:
  314.  
  315. extern char *ltoa(long l, char *str, int base);
  316. extern char *ultoa(unsigned long ul, char *str, int base);
  317.  
  318. The logical place for the ltoa and ultoa functions is in rput.c next to 
  319. the dtoa function.  The code is as follows:
  320.  
  321. /****************************************************************************/
  322. char *                       /* return pointer to string                    */
  323.     ltoa(                    /* convert long number to string               */
  324.         long   l,            /* number to convert                           */
  325.         char  *str,          /* string buffer to use                        */
  326.         int    base)         /* radix of the number                         */
  327. /****************************************************************************/
  328. {
  329.     switch (base) {
  330.     case 8:
  331.         sprintf(str, "%lo", (unsigned long) l);
  332.         break;
  333.     case 10:
  334.         sprintf(str, "%ld", l);
  335.         break;
  336.     case 16:
  337.         sprintf(str, "%lx", (unsigned long) l);
  338.         break;
  339.     default:   /* error */
  340.         *str = '\0';
  341.         rseterr(NULL, EINVAL);
  342.     }
  343.     return str;
  344. }
  345.  
  346. /****************************************************************************/
  347. char *                       /* return pointer to string                    */
  348.     ultoa(                   /* convert unsigned long number to string      */
  349.         unsigned long  ul,   /* number to convert                           */
  350.         char          *str,  /* string buffer to use                        */
  351.         int            base) /* radix of the number                         */
  352. /****************************************************************************/
  353. {
  354.     switch (base) {
  355.     case 8:
  356.         sprintf(str, "%lo", ul);
  357.         break;
  358.     case 10:
  359.         sprintf(str, "%lu", ul);
  360.         break;
  361.     case 16:
  362.         sprintf(str, "%lx", ul);
  363.         break;
  364.     default:   /* error */
  365.         *str = '\0';
  366.         rseterr(NULL, EINVAL);
  367.     }
  368.     return str;
  369. }
  370.  
  371. In recio.h, the value of NSBUFSIZ is used to dimension the internal string 
  372. buffer that the recio library uses when it calls the ltoa, ultoa, and dtoa 
  373. functions.  This value must be large enough to hold every possible number 
  374. (integral and floating point) without overflowing the string buffer.  In 
  375. determining the value, keep in mind the following points:
  376.  
  377.     * Integral numbers contain more digits as the radix gets smaller.
  378.  
  379.     * Reserve space for the exponent of a floating point number,
  380.       including the 'E'.
  381.  
  382.     * Reserve space for possible sign(s).  Floating point numbers can
  383.       contain two signs, e.g. -1.11E-01.
  384.  
  385.     * Reserve space for the decimal point in floating point numbers.
  386.  
  387.     * Reserve space for the terminating null.
  388.  
  389.     * Valuable information on the representation of numbers is contained 
  390.       in your compiler's header files float.h and limits.h.
  391.  
  392.     * A conservative rule of thumb is: make NSBUFSIZ large enough to 
  393.       hold your largest integral value expressed in radix 2.  Thus if 
  394.       your largest integral number is a 32 bit value, make NSBUFSIZ 34.  
  395.       If using 64 bit integral values, make NSBUFSIZ 66.
  396.  
  397. 4.0 REFERENCES
  398.  
  399. Wall, L. and Schwartz, R.L.  Programming Perl.  O'Reilly & Associates,
  400. Sebastopol, CA, 1991, p. 103 ff.
  401.  
  402. Schreiner, A.T. and Friedman, Jr., H.G.  Introduction to Compiler 
  403. Construction with UNIX.  Prentice-Hall, Englewood Cliffs, NJ, 1985, p. 25.
  404.